Leesmij voor !Elc2

Electron emulator voor alles vanaf Archimedes tot RPI 4B+.
 * Wat heeft deze emulator nodig aan systeemvereisten?
Minimaal 2600 K bytes wimpslot. Voor de RaspberryPi is de module Anymode (eigenlijk wel) nodig. En daarbij de uitroep disable_mode_changes in !Boot.Loader.CMDLINE/TXT. Hiermee kan niet-al-te-moeilijk dit programma singletasken.
Singletasking op Beagleboard-XM heeft een probleempje met terugkeer uit singletasking (maar ik heb het bij RaspberryPi ook waargenomen). De oude multitasking screenmode wordt niet meteen opnieuw ge-installeerd. Remedie: Druk F12 nogmaals in, en dan Return. Geen idee wat hierachter zit; het is wel bekend dat schermafhandeling op een Beagleboard verschilt van bv. Iyonix, Rpi omdat BB ook lage kleurdieptes (16,4) aan kan maar of dit ermee te maken heeft weet ik niet.
Op de Archimedes, zelfs met een ARM3, is de emulator nogal traag. Dit komt door de poging om exact te zijn; bij elke Electron schermlijn wordt er 3X iets ge-updated en elke update kost uiteraard tijd. Alternatief is om een speciale versie te maken die de exactheid overslaat. Daarnaast is de ARM3-cache van 4K klein, de meest-frequente instructie afhandelingen en data passen er samen net niet in. Vanaf RiscPC 710 begint de snelheid acceptabel te worden. Gebruik in de !Run file voor de Archimedes een wimpslot van -min 2700K -max 2700K en gebruik de crunched versie van !Runimage (!RunImage').
Omdat voor het geluid SharedSound wordt gebruikt met een dynamic area, en Archimedes geen DA's kent en RISC_OS_Linux_binary nog geen geluid heeft, is geluid niet mogelijk op Archimedes of Linux. Dit is daar het best uit te zetten door in de !Run file de RMEnsure Sharedsound .. regels weg te halen of er | 's voor te zetten.
Eigenlijk moeten er nog 2 aparte prog'jes bij, een om (op een Archimedes, want alleen die kan single-density diskettes aan) diskettes te kunnen inlezen naar images, en een om cassettebandjes (die je dan eerst zelf moet samplen naar een Riff_WAVE) om te zetten in een UEF. Van deze aparte applicaties heb ik het merendeel van de code al maar ze zijn nog totaal niet gebruiksvriendelijk. En de meeste UEF's en diskette-images zijn toch al kant en klaar te koop of te downloaden (resp. www.acornelectron.co.uk en op archive.org); zo'n utility lijkt me ook meer op zijn plaats in een algemene wave-editor.

Configuratie.
Het is mogelijk dat je de eerste keer alles moet configureren; op iconbarmenu Config kiezen, als alles naar wens is klik dan Save. Als het programma !Boot.Choices kan vinden, dan wordt daar een Elc2 map aangemaakt en de configuratie daar weggeschreven. Op hoofdwindow, als deze intussen open is, vervolgens op linkerpane Reset indrukken, dan worden de gekozen waardes meteen doorgevoerd. Wegschrijven van de keuzes moet expliciet gebeuren, dus in Configuratiewindow altijd op Save klikken. Als je niet op Save drukt worden bj een Reset wel je keuzes gevolgd maar blijven bij een volgende keer opstarten de gesavede keuzes actief. Standaard staat er geen disk filing systeem aan. Het gemakkelijkst is om DFS te kiezen.
Extra hulp is te bekomen door !Help (RiscOS applicatie voor interactieve hulp, meestal in Apps) te activeren. Deze is te vinden op de iconbar (van de gastcomputer) onder het icon 'Apps'. Het is een applicatie in ROM dus je vindt hem niet in $.Apps of in $.Utilities.

Media: Disks
Zowel ADFS als DFS zijn te gebruiken. Via Config zijn deze te selecteren. De DFS versie is er een, waarbij er niet alleen een ROM op de cartridge aanwezig was maar ook een 4 kilobyte RAM. Dit maakte het mogelijk om PAGE op &E00 te houden. ADFS neemt een ruime hap uit de toch al geringe hoeveelheid RAM zodat veel programma's vanaf tape, ruimte tekort komen; op ADFS zit PAGE op &1D00 en op deze DFS op &E00. Om cassette als input te kiezen, type dan *TAPE in.
Bij DFS gebruikt men *DRIVE <n> om een diskette te 'mounten' en *EXEC !BOOT, CHAIN "!BOOT", en varianten om de zaak te starten. Met <n> is een getal, 0 of 1 of 2 of 3. 0 of 1 zijn eerste kant van de diskette, 2 of 3 is de andere diskettekant. 0 en 2 betreffen de diskette in de bovenste diskettebay, 1 en 3  wijzen op de onderste diskettebay. DFS is de naam van het disk filingsysteem maar je hebt ook nog de aanduidingen SSD en DSD die wat zeggen over de diskette. Of er relevante data op een kant staan, of aan beide kanten. SSD betekent Singlesided Diskette, DSD is Doublesided. De manier waarop de sectoren van beide kanten in een image van 400K worden opgeslagen wisselt heel erg. Deze emulator begrijpt DSD images met de eerste kant aaneengesloten tussen adressen 0 en 200K, andere kant 200K-400K en tevens de methode waarbij tracks elkaar afwisselen, dat wil zeggen 10 sectoren (van 256 bytes, dus 2560 byte grote blokken) van kant 1, dan 10 sectoren van kant 2, dan weer 10 van kant 1, daarna 10 van kant 2 enzovoorts. DSD's met een map van 4 sectoren aaneengesloten groot worden niet begrepen. Omdat bij DFS de bestanden in principe aaneensluitend vanaf het begin worden opgeschreven, is het toegestaan dat de lege sectoren in de image ontbreken. Dus een 200K of 400K diskette mag een veel kortere image hebben, als er maar wat staat op die sectoren die in de map worden verwezen. De emulator vult deze ontbrekende sectoren automatisch aan.
Bij ADFS zijn de commando's respectievelijk *MOUNT <n> met <n> is drivenummer bv, 0, 1, 2, of 3, en (ook weer) *EXEC !BOOT of CHAIN "!BOOT"
*EXEC !BOOT gebruikt men als het bestand !BOOT een executable is, CHAIN "!BOOT" als !BOOT een BASIC bestand is.
Als er geen !Boot is maar losse bestanden met een naam bv. SHUGGYS, doe dan *EXEC SHUGGYS of CHAIN "SHUGGYS" (alleen het laatste zal werken in dit voorbeeld, want SHUGGYS is een BASIC bestand voor het spelletje Shuggys Garden - het zit in map Files)
Catalogiseren gebeurt met *CAT, soms werkt *EX ook.
Voor het formatteren is meestal een utilities disk nodig. Zoals de Welcome ADFS disc. In de documentatie hierbij zal vast wel uit te vinden zijn welke parameters men moet meegeven. Dezn zijn nog primitief dus anders dan in b. Riscos 3.10 en volgende.
Het aantal te gebruiken diskettes (1 of 2) is in te stellen via Configure (op het iconbar menu van !Elc2). Hier is ook de ruimte in te stellen voor de cassette UEF's. Het programma gebruikt alleen geheugen dat met de !Run file (in !Elc2 directory) is meegegeven en gebruikt geen dynamic areas, behalve dan voor de sharedsound geluids produktie. Het vermijden van Dynamic Areas heeft ermee te maken dat dit !Elc2 programma ook voor een Archimedes of vergelijkbaar met Riscos 3.10 etc. was bedacht en Riscos 3.10 is nog onbekend met het idee van Dynamic Areas; dynamic areas (DA's) zijn lappen geheugen die buiten de eigenlijke wimpslot van je programma kunnen worden geclaimd en vrijgegeven. De !Runimage grootte, circa 640K, is door crunchen te reduceren tot ongeveer 270K wat dan weer extra ruimte oplevert voor diskettes (per stuk 1000000 bytes nodig) en cassette (krijgt de overblijvende ruimte nadat programma, werkruimtes en diskettes van de wimpslot zijn afgehaald). De wimpslot is aan te passen in de !Elc2.!Run file met een teksteditor als !StrongEd of !Zap, maar dit zal bekend zijn. 4000K is voor alle toepassingen wel in orde, maar voor een 4Mb Archimedes werkt alleen 2600K of 2700K en gebruikmaking van de gecrunchte !Runimage'. Crunchen is mogelijk met !Reggraph. Alle opties selecteren behalve 'variabelen in DATA lijnen', en verder de Excludefile (uit de !Elc2 map) vooraf op het crunchwindow trekken.
Onder het menu op een lege diskettebay zit de mogelijkheid een voorgeformatteerde floppy aan te maken. Dat bespaart het opzoeken van een utilities floppie met FORMAT en varianten.

Media:UEFs
De UEF's die je op internet vindt zijn veelal gezipte bestanden. !Elc2 kan ze unzippen met hulp van !SparkFS (ook de gratis read-only versie in $.Utilities). Infozip schijnt ook te kunnen, zelf niet geprobeerd. Of trek het gezipte bestand op !SparkFS en de ge-unzipte versie ervan verschijnt in dezelfde directory als waar de gesleepte file vandaan kwam. Achter de nieuwe naam staat dan vaak '00'. In !Zap (etc) kun je dan wel kijken of in het ontstane bestand er een header zit bestaande uit het woord 'UEF File!'
UEF's (Unified Emulator Format) is een protocol om met name de geluiden op programma-cassettes om te werken tot een 'image', een bestand dat hetzelfde kan doen voor de emulator als een cassettebandje met speler voor een echte Electron. Sleep de UEF op het cassette icon op de pane links, en een cassette window opent. In hoofdwindow kun je dan intypen *TAPE en vervolgens CHAIN"" waardoor het eerstvolgende bestand (indien BASIC) op het 'bandje' (lees: UEF) wordt ingeladen en gedraaid. Als het een executable is doe dan */ of *exec <bestand> met <bestand> de naam van het bestand op het bandje. Via een menu-entry (rechtsboven) op het cassetterecorderwindow zijn de afzonderlijke bestanden, of alles tegelijk, te saven als bestand, als UEF of als een RIFF Wave-bestand.
Door te klikken op de cassettewieltjes kun je voor-of achteruit spoelen. Via een menu is de cassette te legen.
Zoals gezegd, de meeste UEF's zijn gezipt. Om ze te openen moet !SparkFS of een ander programma dat '*unzip <inputbestand> <outputbestand>' begrijpt, actief zijn.

Media: File rechtstreeks op Mainwindow laten vallen
Een laatste manier om bestanden in te laden is, door ze rechtstreeks op het hoofdwindow te trekken. Als het programma niet geheel zeker van de te volgen actie is, volgt een dialoogwindow met de keuze Execute, Save to current directory of Just Load. Het kan zijn dat bij/na deze acties het inlaad- en uitvoeradres nog moet worden ingevoerd, in hexadecimale notatie. Voor een programma dat als BASIC file moet worden uitgevoerd is het inlaadadrs meestal &E00 (&1D00 bij ADFS) en executieadres is &8023 (is het startadres in de BASIC ROM).

De Pane aan de linkerzijde van het hoofdscherm.
Kort even langs de opties op het linker pane:
Keyboard: Toggel tussen Amerikaans, Engels en Electron keyboard. De toetsen die je indrukt worden vertaald naar Electron toetsen; zoals bekend zitten vele Electron toetsen helemaal anders dan bij de toetsindeling die paslater bij de PC's standaard werd. Het programma doet de vertaalslag. Alternatief kan met rechts-klikken op Keyboard een virtueel Electron toetsenbord worden opgebracht. 
Monitor: Om programma's te singlesteppen. In beperkte mate kan je ook achteruitsteppen, dwz het programma onthoudt van de laatste circa 22 instructies de waarden van accumulator, X, Y, stackpointer etc. Zie de beschrijvingen met interactieve hulp ('!Help', zie boven) geactiveerd.
Reset: Zoals de naam zegt. De meeste, maar niet alle, veranderingen in Configure window worden pas actief na een reset. Een enkele config optie wordt zonder reset actief, bijvoorbeeld de singletasking-screenmode wordt rechtstreeks uiot Configure uitgelezen zonder dat er een reset nodig is.
Sound: ON en OFF met linker muisknop, amplitude slider window met rechter muisknop. Werkt op dit moment alleen met SharedSound, en niet op Archimedes. SharedSound gaat over RBMemMgr voor de interface en RBMemMgr heeft dynamic area's nodig, die de Archimedes niet begrijpt. Dus vandaar. Als ik iets snapte van hoe 8-bit geluid werkte dan Archimedes geluid mogelijk zijn, - maar Archimedes is toch te langzaam voor deze emulator dus 'why bother'.
Speed: Rechts klikken/ingedrukt houden verhoogt de snelheid, links klikken zet terug naar 1X. Redelijk ideaal om bv. Acornsoft Chess te spelen of de wachttijd voor UEF's te bekorten.
Cassette: Opent als je er een UEF op sleept. Het menu-icoontje geeft uiteindelijk toegang tot save-opties onder andere saven als Riff-wave. Gebruik bv. !Playit of !DigitalCD om deze waves af te spelen; je kunt de audio-uitgang van je gastcomputer verbinden met de cassette ingang van een echte Electron, Master of BBC zodat je deze emulator als cassettespeler kunt gebruiken. De amplitude van de wave is te varieren en ook de relatieve fase van 1200 vs. 2400 Hz; alleen nog niet de relatieve amplitude van 1200/2400. Voor mijn Electron moet de 2400Hz-fase iets naar rechts worden gezet, en amplitude maximaal, anders 'pakt' de Electron het niet. De wavesave is (anders dan de emulatie zelf) niet machinespecifiek dus dit geluid naar BBC of Master brengen zou mogelijk moeten zijn. Bedenk wel dat niet alle audio-uit aansluitingen van de diverse computers waar RiscOS op draait, even sterk zijn. Iyonix is vrij zwak en vervormd (clipt) bij hoge amplitude, A9home is sterk, RPI moet het redelijk aan kunnen.
Plus-1: Opent op dit moment alleen een cartridge bay. Je kunt hier ROM-images in slepen bijvoorbeeld de Demo cartridge, of Pascal, Comal, View etc. Sommige ROMS kwamen in een paar, zoals Pascal 1 en 2; het programma laadt in voorkomende gevallen de tweelingrom automatisch. Mits deze ook in de ROMS subdirectory staat. Op elke rom staat een omschrijving en een menu erboven maakt het mogelijk de cartridge te verwijderen. Printeraansluiting is nog niet werkend; Joystick aansluiting werkt inzoverre de muispositie als joystickingang is in te stellen. Het werkt nog niet met de USB-joystickmodule.
Floppies (de twee blauwe diskettes): Opent een window met een of twee diskettebays. Op een lege bay zit een menu om een geformatteerde floppy aan te maken. Je kunt /ssd of /dsd of /adf type images op het diskette-icon op de linkerpane slepen (suffix (/ssd etc) niet verplicht, ook filetype zoals text, data, &fce etc wordt niet naar gekeken), of rechtstreeks in een diskettebay op de geopende diskettewindow. Bij adfs diskettes met twee kanten kunnen de kanten in het image alternerend zijn opgeslagen dan wel sequentieel. Dat wil zeggen: kant 0,track 0, kant 1 track 0, kant 0 track 1, kant 1 track 1, kant 0 track 2.. is alternerend; kant 0 track 0, kant 0 track 1, kant 0 track 2 ,,, kant 0 track 79, kant 1 track 0 kant 1 track 1... is sequentieel. Sommige images worden 'verkeerd om' aangeleverd en die kun je via een menu optie wisselen zodat ze (hopelijk) worden herkend. 
Singletask. Wat de naam zegt. Men kan het beste 640X512 kiezen, Bij RPI's met Anymode aan, wordt dit geaccepteerd. Maar de laatste jaren is er een trend om de monitor (dwz. het weergave-apparaat met het grote LCD-scherm) de resolutie te laten kiezen, inplaats van dat de MDF's dat doen (EDID, zogezegd), en staat 640X512 niet meer tussen de aangeboden resoluties of wordt deze resolutie niet meer geaccepteerd. Daarom is 800X600 toegevoegd, die iets couranter is. Men kan de keuze maken in Configure. Terugkeren uit singletasking gaat met F12.
Test OFF/ON. Met rechts klikken opent een window waarmee men diagnostische dingen kan doen. Zoals traps zetten voor bepaalde PC locaties, instructies etcetera. Hoevaak bepaalde instructies voorkomen, wordt in deze stand bijgehouden. Zodra je voormelde optie "TEST" aanklikt wordt de executiecode opnieuw geassembleerd en worden diverse extra checks toegevoegd (alles gaat dan ook nogal wat langzamer). Als e.e.a. even heeft gelopen in deze Testmode, is er genoeg bekend over de relatieve instructiefrequenties en kan (rechts)geklikt worden op 'Opcodes', de executie-'codelets' worden dan opnieuw gegroepeerd zodat de meestgebruikte daarna vlak bij elkaar staan. Dit vermindert het aantal 'cache misses' en draagt bij aan de uitvoeringssnelheid, tenminste zodra TEST weer OFF is gezet. En de cache van de gastgevende computer niet al te extreem klein is - want in dat geval helpt zelfs deze truuk niet.

Wegschrijven van 'savestate'
Het is mogelijk om zo ongeveer de volledige toestand van de emulator op te slaan en terug te halen voor een volgende keer. Dit wordt typisch opgeslagen in een directory waaraan je zelf een naam kunt geven, en daarin bevindt zich dan een 'savestate' bestand en waar van toepassing (verwijzingen naar) UEF- of floppy images. Via Config is e.e.a. zo in te stellen dat bij afsluiten van het programma, de toestand wordt opgeslagen (in <Elc2$Dir>.DUMP) en bij de volgende keer opstarten wordt de oude instelling weer teruggehaald. Een directory of een losse savestate file kan op hoofdwindow of op iconbarmenu worden getrokken en hopelijk start die opgeslagen toestand dan weer op zoals hij was achtergelaten. De emulator heeft wel graag dat eventuele UEF's en diskettes zoals genoemd in de savestate file, bij weerom-activeren nog precies op dezelfde relatieve plaats staan.

Meer SSD's, en UEFs.
Vele SSD's en UEF's vind je hier:
https://archive.org/details/Acorn_Electronic_TOSEC_2012_04_23
(for completeness - de Archimedes en BBC equivalenten zijn -
https://archive.org/details/AcornArchimedes_201809
https://archive.org/details/Acorn_BBC_TOSEC_2012_04_23
)
www.acornelectron.co.uk. Naast downloaden, zijn hier zijn DVD's of een 64 Gb USB stick te bestellen met teveel om in een mensenleven te spelen of door te lezen.
Op internet is nog wel meer te vinden bv. www.stairwaytohell.com is een heel bekende.
Op www.stardot.org.uk staan ook wel verwijzingen genoemd, ook naar geheel nieuwe spellen en demo's.
Sommige dingen op www.bbcmicro.co.uk zijn ook wel op een Electron te spelen.

Informatie.
Heel veel informatie is te vinden bij chrisacorns.computinghistory.org.uk/Computers.html. Onder andere over hardware informatie, schema's etc. Als ze daar niet ook bij staan kunnen de specifieke onderdelen zoals 1770/ 1772 chip (floppydisc controller) op internet worden gevonden. De Electron heeft op de 6502 processor na, eigenlijk geen onderdelen van derden waar ook nog informatie over te krijgen is; alles waarvoor dit soort chips nodig waren (geluid, video, etc) in de BBC computer, zit in de ULA (en dan ook nog in vereenvoudigde vorm).
Boeken met programmeerinformatie o.a. de Advanced User Guide staan o.a. op een CD uitgebracht door Chris Dewhurst van het Drag&Drop PDF magazine. www.dragdrop.co.uk

Technische info.
Het volgende is alleen wellicht van nut voor mensen die zich afvragen hoe het onder de motorkap werkt.
(inleiden, 6502, instructiebreedte, registers, ULA en wat die doet, Plus-1, floppydisc controller). 
Emulatie=het zo goed mogelijk nadoen. Dit is niet hetzelfde als simulatie. Idealiter hoort emulatie je de 'look and feel' van het origineel te geven. 
Het emuleren van een computer vereist het emuleren van de processor (uiteraard!) maar ook van de andere chips in die computer. Het emuleren van de rand-chips zoals voor floppies en geluid, en vooral voor de aansturing van de video, is vaak een stuk meer problematisch dan voor de processor; waar de aanpassing aan meer moderne displays nodig is, gaat emulatie zelfs over zaken die niets meer met de originele computer van doen hebben. 
De processor.
De 6502-processor heeft zo'n 150 officiele instructies en daarnaast een aantal niet-gedocumenteerde. Die laatste zijn het gevolg van, zo te zien, losse eindjes in het hardwarematig decoderen van de instructie. De lengte van de instructies is niet altijd gelijk, deze kan 1,2 of 3 bits zijn, maar altijd is de eerste byte de opcode (wat moet het doen?) en waar nodig kunnen 1 of 2 bytes daarna als 'operand', (de gegevens waarmee gewerkt moet worden), worden aangewend. De 8 bits voor de instructie geven 256 (2^8) mogelijkheden; er zitten als het ware gaten in deze 256 grote ruimte. Ook in de ARM2-processor, die erg is geinspireerd door de 6502, vind je dit soort gaten; maar een en ander is anders opgelost. De LDR en STR instructies daar, bijvoorbeeld, kunnen geen right-hand-side operand gebruiken die verschoven is met de inhoud van een vierde register 'shift' (de andere 3 zijn daar destination dest, righthandside rhs, lefthandside lhs). Je hebt bv. ADD dest,lhs,rhs,LSL shift maar geen LDR dest,[lhs,rhs,LSL shift] (met 'shift' = een register-aanduiding). Daar valt dus een gat en veel van dit soort gaten leiden op de ARM processor tot een illegal-instruction-exception. Dat is handig want dit is te gebruiken om een processor te stoppen die onbedoeld foutieve code aan het uitvoeren is. Niet aldus op de 6502; niet-gedefinieerde instructies kunnen allerlei wonderlijke uitwerkingen hebben en die zijn in de 6502-processors van diverse fabrikanten ook niet alle gelijk. De meest beruchte is de 'hlt' groep, deze stoppen de 6502 helemaal, wat neerkomt op een crash. Ik heb geprobeerd de niet-gedocumenteerde instructies voor de Rockwell 6502 te emuleren, en de 'hlt' zou dan niets moeten doen maar om toch enige indicatie te geven wat er mis is, opent 'hlt' de singlestep-window.
De 6502 heeft vrij veel adresseermogelijkheden: implied, immediate, absolute, absolute-X, absolute-Y, zeropage, (zp-X), (zp),Y, indirected, relative zijn er al tien. Dan zijn er 5 registers A, X, Y  en P (=processorstatus) en S (stackpointer), en instructies als ADC, SBC, EOR, ORR, AND, BIT, CMP, LDA/X/Y, STA/X/Y, INC, DEC ,Bxx, JMP, JSR, RTS en nog veel meer. Het produkt Adresseringen (10) maal registervariant (5) maal instructietype (circa 50) gaat ver uit boven de 256 opcodemogelijkheden die een 8-bits opcode geeft. De adresseringsmogelijkheden voor de wat minder courante instructies zijn daarom kennelijk ingeperkt. Je moet er een kaart bij hebben om te zien welk slinks pad je moet bewandelen om iets voor elkaar te krijgen. Dat maakt programmeren op een 6502 veel omslachtiger dan op een ARM. Als je bijvoorbeeld de stapelwijzer 10 lager wilt hebben doe je op ARM: SUB sp,sp,#10. Op 6502 wordt dit:TSX:TXA:SEC:SBC#10:TAX:TXS. Met het schrijven van een emulator krijg je er in zoverre mee te maken dat af en toe testprogramma's moeten worden geschreven om de werking van de emulator te vergelijken met de echte machine. Het schrijven van een emulator lukt goed als je eerst een kleine emulatie maakt dat een bekend programma uitvoert; hiermee kun je experimenteren welke emulatiemethode het snelste werkt. Vervolgens implementeer je alle overige instructies en controleert ze een voor een met controlecode op de echte machine. Het correct krijgen van de instructie-emulaties is essentieel om straks, als je bv, diskette code schrijft, geen onoplosbare problemen te krijgen. 
Onontwijkbaar voor een emulatie is dat je een bepaalde hoeveelheid geheugen nodig hebt om de uitvoerende code voor de emulatie in te passen, en elk van deze uitvoerings-stukjes is zo kort mogelijk (want alles samen moet in de cache passen), dus de emulerende processor moet heel vaak heen en weer springen. De volgende instructie kan elk willekeurig andere zijn dus het is niet goed te voorspellen naar welk adres straks gesprongen gaat worden. Moderne processors rekenen alvast wat resultaten vooruit door beide afslagen bij een conditionele sprong tegelijk te nemen en achteraf de resultaten in de foutieve tak weg te gooien (speculatieve executie). In een emulatie van een andere processor komen heel veel sprongen voor (minstens 1 per emulatie, bij een interpreterende emulator als deze) en zijn die ook nog eens vrijwel onvoorspelbaar. Het minste dat je kunt doen om de uitvoerende processor te helpen is, (1) de emulatie-codeblokjes in de primaire cache te laten passen en (2) het aantal sprongen in de instructie-emulatie te beperken. Zero-page en immediate instructies slechts een enkele sprong geven, namelijk alleen daar waar de nieuwe instructie wordt opgehaald. En de meest voorkomende instructies zijn bijeengezet zodat ze samen hopelijk in de primaire cache passen. De processoruitvoeringscode is beperkt tot minder dan 8 Kbyte en dat zal wel passen in de meeste moderne ARM-processor caches. De instructies die met 'absolute' adressen werken, gebruiken twee stappen. Primair worden eerst 2 bytes opgehaald en daaruit het absolute adres berekend en secundair wordt naar de eigenlijke uitvoering gesprongen, waar dit adres wordt gebruikt bijvoorbeeld om data vandaan te halen. Deze absolute, absolute,X en absolute,Y etcetera berekeningen vergen veel berekeningen. Als je ook alle 'absolute address' codes zou uitschrijven dan past het niet meer in de cache, en wordt de uitvoering trager omdat er te vaak code in de cache moet worden ververst (cache thrashing). Dat wil je ook weer niet. Het is een afweging: hetzij compacte code die in de cache past, dan wel iets grotere code met minder sprongen die niet meer in de cache past. Absolute-adresserende instructies zijn niet de meest voorkomende.
Voor de rest is geprobeerd om 'stalls' te voorkomen: data-operaties krijgen 1 tik extra voordat destination wordt gebruikt als bron-register in een volgende instructie, loads en stores krijgen er 2. Ophalen en uitvoeren van een instructie, bijhouden van de event-teller (.docounter) en ophalen van de volgende instructie zijn 3 bezigheden die in verschillende registers kunnen worden ondergebracht en dus tot op zekere hoogte dooreen zijn te vlechten waardoor je stalls (opstoppingen) kunt voorkomen. Maar dat lukt niet overal. 
Voor de 6502 is het geheugenbereik 64K (65536 bytes). Een deel daarvan, het bereik &8000-&BFFF, kan worden omgeschakeld tussen 16 aparte ROMS. Lijkt ruimhartiger dan het is, BASIC neemt 2 posities in beslag en het toetsenbord ook twee. In de ULA (het losse chipje in een vierkante behuizing onder een metalen klepje) zit een register waarmee je kunt omschakelen. Vanwege deze omschakeling moet er iets zijn waarmee de processoremulatie nog steeds blijft werken wanneer er van ROM wordt gewisseld; een heel blok van 16K bytes naar adres 8000-BFFF verplaatsen, elke 1/50 seconde, kost teveel tijd. In dit geval is een soort memory-manager opgetuigd. Voor de instructies die een 16-bit adres gebruiken wordt de uiteindelijke data in 2 stappen binnengehaald. Eerst wordt van het adres de pagina (0..255) bepaald, het most-significant-byte (MSB, bits 8..15 van het 16-bits adres) is wat we nodig hebben. Via een wijzer naar een memory manager array (mmu) wordt een 32-bit word opgehaald, dat bestaat uit 4 stuurbits (in bits 28..31), en de eigenlijke offset in 0..27. Deze offset, 4 linksom geschoven waardoor je de stuurbits kwijt bent, wordt bij het eigenlijke adres opgeteld. Dus: AND offset,address,#&FF00:LDR offset,[mmu,offset,LSR#6]:STRB data,[address,offset,LSL#4]. Daarbij is de inhoud van mmu[page<<2] = (rambase (=adres van 'Electron RAM/ROM') - (load/store_adres AND&FF00))>>>4. Zeropage kan rechtstreeks worden benaderd met [rambase,load/store_address]. Wat je in [load/store_address,offset,LSL#4] dan krijgt is het adres waar de emulator de gewenste data heeft zitten. Vanaf het resulterende adres wordt de data opgehaald of weggeschreven. Een schrijfactie naar het ROM-page-register in de ULA herberekent de 64 offsets voor pagina's &8000-&BF00, (dus memorymanager entries 128-191) zodat de mmu-adressen nu naar de desbetreffende ROM wijzen. Als wijzer naar ge-emuleerde RAM, memory-management array en naar overige variabelen is 1 register genoeg, positieve offsets wijzen naar RAM, negatieve offsets -0..-1024 zijn de mmu offsets en negatief -1024 tot maximaal -4092 wijst naar variabelen. Dus [rambase,#0..65525] voor de ge-emuleerde RAM, [rambase,-offset,LSR#6] voor de memorymanager en [rambase,#-1024..-4095] voor variabelen. Op dezelfde manier wordt een (ander) register tweezijdig gebruikt voor offset naar een instructie-emulatie en basisadres van deze emulaties.
Vanwege de mogelijkheid (zie boven; TEST window) om de 256 emulaties op een andere manier te groeperen is de code daarvoor onoverzichtelijk geworden. Maar in principe moet het niet al te moeilijk zijn deze code te hergebruiken om een andere vintage computer mee te emuleren; alleen de code voor perifere chips (video, PIA's) hoeft dan in principe te worden aangepast.
Alle timing-afhankelijke acties gaan via een timing array met 16 entries. Omdat de Electron tamelijk eenvoudig is, zijn 16 entries voldoende. Denk aan aflopen van 50Hz-interrupt, vertical sync-interrupt, en interrupts die nodig zijn om cassette, floppydisc goed te laten verlopen.
Voor deze perifere chips en events gebruik ik een toestands-machine. Deze werkt in halve-microseconde eenheden. Er is een lijst met zaken die moeten worden afgehandeld, parallel aan de instructie-uitvoering zelf. Enerzijds zijn dat timers, (vsync en 50 Hz), anderzijds zaken die in perifere chips gebeuren, waartoe ik ook de ULA (geluid, cassette, toetsenborduitlezing, video) en de floppydisc-controller reken. Er is dus een lijstje met events waaronder een null-event (aanwezigheid hiervan is nodig voor 't geval de lijst op enig moment leeg is). Als er in de toekomst iets moet gebeuren, dus als een event x over y halve-microseconden moet gebeuren, wordt die tijdsafstand y samen met het event-nummer x in de lijst ingevoerd ('settimer'). De lijst bestaat uit tijd-offsets vanaf de entry die nog urgenter is, dus bij het invoeren worden alle reeds aanwezige entries gepasseerd totdat entry x (niet later maar) eerder moet komen dan de volgende. Ingevoerd wordt dan het entry-nummer (4 bits) en de tijdsafstand tot de laatst gepasseerde entry. De null-offset komt altijd achteraan de lijst. De meest urgente gebeurtenis komt terecht in een register 'counter' die bij elke 6502-instructie-afhandeling wordt verminderd met de tijd die deze 6502 instructie nodig heeft. Een nieuw event kan dus ook 'counter' verkleinen omdat hij nog urgenter is dan de event die tot dusver op 'counter' stond. Zodra counter door 0 heen gaat, wordt naar de docounter routine gesprongen en bekeken wat er moet worden gedaan (dotimer_xxx), en wordt de volgende event op de counter gezet. In de code vind je dit mechaniek als SUBS counter,counter, #..:BLMI docounter. 
Een perifere chip kan meerdere timer-entries hebben. Zo heeft de floppydisc er twee, 1X voor de toestanden waarin de floppydrive zich kan bevinden terwijl hij een track uitleest, en 1X voor de vordering in de afhandeling van het aan de disc-controller aangeboden commando. Ook deze methode maakt het mogelijk vele dingen schijnbaar parallel te doen en is algemeen toepasbaar.

Tot de meest ingewikkelde zaken om te emuleren behoort het video-gebeuren. Moderne 'host' computers sturen nogal eens een hogeresolutie-monitor aan, terwijl de Electron komt uit een tijd waarin deze ondenkbaar waren. Om de lage resoluties (160*256, 8 kleuren) om te zetten/ te expanderen naar 1920X1080 is heel veel processorcapaciteit nodig, en uiteraard moet je dit tot het minimum beperken dan wel de hardware van hostcomputer het zelf laten doen, bijvoorbeeld met !Anymode. Ik heb me (voor singletasking althans) beperkt tot vier 'standaard' expansies namelijk 640X256, 640X80, 640X512 en 800X600. Die zijn in de configuratie aan te vinken. (Voor multitasking wordt altijd 640X512 of 640X256 gebruikt). De RaspberryPi met Anymode ge-installeerd, zal in singletasking beide (zowel 640X512 als 800X600 als 640X480) wel kunnen weergeven; Anymode verplaatst al het rekenwerk hiervoor naar de Broadcom-GPU. Voor moderne LCD monitors is een iets hogere framerate dan de 50 Hz van de Electron nodig, en op de Archimedes als emulerende hostmachine een VIDC enhancer voor de wat grotere schermresolutie. 
Met Interlace AAN (zie in window: configuratie) worden de beeldlijnen alternerend even en oneven ge-updated in de sprite die de emulator intern gebruikt. Met interlace OFF wordt intern een 640X256 sprite gebruikt en de computer zet die om naar het scherm. Het programma bekijkt waar in het schermgeheugen er iets is veranderd en rekent alleen deze gedeeltes om naar de uiteindelijke sprite; houdt ook bij waar veranderingen beginnen en waar ze stoppen en ververst alleen alles tussen die grenzen naar het scherm. Gelukkig is de 6502 niet zo snel; bij het emuleren van een Archimedes, ARM2/ARM3 op een displaymonitor op meer dan 50 Herz, krijg je ondanks deze voorzorgen al snel te maken met 'tearing', het effect dat de schermupdate van de emulator niet gelijkloopt met de schermverversing van de gastgevende computer - waardoor je hinderlijke horizontale discontinuiteiten ziet. Dat laatste is met 'double' of (op Rpi) 'triple buffering' op te lossen maar probleem wordt dan dat je scherm te ver achterloopt op de emulatie, en spelletjes waar snelle reacties nodig zijn, dan niet meer goed werken. Vanwege die traagheid van de Electron heb ik van elke vorm van double of triple buffering afgezien, want zo te zien onnodig.
Er is een aantal mogelijkheden om het Electron schermgeheugen naar het moderne scherm over te brengen, maar het is altijd een afweging tussen efficientie en exactheid. Om dit op een trage machine als een Archimedes zo te emuleren dat ook nog bepaalde demo-effecten zijn te doen, is vrijwel onmogelijk. Nog een probleem met de Archimedes met ARM3 is, dat de uitvoerende code en de data 'uit de cache lopen' want de cache is maar 4K groot. De effectieve snelheid loopt daardoor terug naar 8 MHz of minder. (De Archimedesversie rekent in singletasking uitsluitend met 16-kleuren, zie verderop)
Bij elke lijn moet worden gekeken: is het Electron-display-adres dat de vorige keer bij deze lijn hoorde, veranderd? Is er in het geheugenbereik van deze lijn in de tussentijd geschreven? Is de palette veranderd t.o.v. de vorige keer? En als alle kleuren zwart zijn en dat voor deze lijn de vorige keer ook al gold, hoeft een lijn helemaal niet te worden vertaald.
Als compromis is ervoor gekozen om bij een palette-verandering, deze palette bij de desbetreffende lijn op te slaan, en bij het vertonen van elke lijn te kijken of de palette al is omgerekend. Momentele palette en Electron-kleurdiepte worden verrekend naar een set van 2 of 4 words voor elk Electron-byte. De 256-kleuren-tussensprite-palettes blijven constant. Door 8bpp te kiezen werkt het op alle machines*. Om teveel rekenwerk voor te zijn worden er meerdere tabellen gemaakt (16 stuks), telkens als er een palette- of mode verandering is wordt bekeken of de omrekening al bestaat. Zo ja, dan wordt deze gekozen (=omzetten van een wijzer). Zo nee, dan wordt deze opnieuw berekend. Er zijn meerdere omrekeningen, de meest-gebruikte komen vooraan te staan en de laatste, de minst-gebruikte, wordt weggegooid als er ruimte nodig is voor een nieuwe palette-omrekening. Verder worden de pagina's (elk 256 bytes groot) waarin je het geheugenbereik kunt verdelen, getagd zodat wanneer er niet naar is geschreven, die pagina ook niet hoeft te worden vertaald. Bij elke lijn wordt de voortgang van de emulatie 3X onderbroken om de lijn zonodig te vertalen naar de tussensprite. Dit is veel meer werk maar vanaf de RiscPC, ARM700, kunnen gastcomputers het werk wel aan.
*sinds de vorige versie is ook een displayroutine gemaakt die intern met 4bits-per-pixel werkt; dit scheelt de helft aan kopieerwerk en dat is merkbaar voor de Archimedes. Bovendien is er een singletasking mode bijgekomen: 640X480. Dit laat elke 16e lijn weg, het valt meestal niet op. Want de Archimedes met VIDC enhancer, kan wel 800X600 genereren maar de framerate is te laag, wordt niet herkend door veel LCD monitoren. 640X480 (MODE 27 of 28) lukt vaak wel.
Verder is er niet zoveel over te zeggen behalve dat ik nodig eens met iets anders aan de slag moet. Zoals Hippocrates al zei: Vita brevis, ars longa.
Voor de rest: Enjoy!!

Jan de Boer, email:jandeboer@telfort.nl     website:www.tellima.nl/riscos/  December 2021/mei 2022.



